﻿using Furion;
using Furion.DatabaseAccessor;
using LuoYi.Core.Dtos;
using MapsterMapper;
using Microsoft.EntityFrameworkCore;
using System.Linq.Dynamic.Core;

namespace LuoYi.Core.Services
{
    public interface ICrudService<TEntityDto, in TKey>
        : ICrudService<TEntityDto, TKey, PagedRequestDto>
    {

    }

    public interface ICrudService<TEntityDto, in TKey, in TGetListInput>
        : ICrudService<TEntityDto, TKey, TGetListInput, TEntityDto>
    {

    }

    public interface ICrudService<TEntityDto, in TKey, in TGetListInput, in TCreateInput>
        : ICrudService<TEntityDto, TKey, TGetListInput, TCreateInput, TCreateInput>
    {

    }

    public interface ICrudService<TEntityDto, in TKey, in TGetListInput, in TCreateInput, in TUpdateInput>
        : ICrudService<TEntityDto, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
    {

    }

    public interface ICrudService<TGetOutputDto, TGetListOutputDto, in TKey, in TGetListInput, in TCreateInput, in TUpdateInput>
    {
        Task<IReadOnlyList<TGetListOutputDto>> GetListAsync(TGetListInput input);
        Task<TGetOutputDto> GetAsync(TKey id);
        Task<TGetOutputDto> CreateAsync(TCreateInput input);
        Task<TGetOutputDto?> UpdateAsync(TKey id, TUpdateInput input);
        Task DeleteAsync(TKey id);
    }

    public abstract class CrudService<TEntity, TEntityDto, TKey> :
        CrudService<TEntity, TEntityDto, TKey, PagedRequestDto> where TEntity : class, IPrivateEntity, new()
    {
        protected CrudService(IRepository<TEntity> repository) : base(repository)
        {
        }
    }

    public abstract class CrudService<TEntity, TEntityDto, TKey, TGetListInput> :
        CrudService<TEntity, TEntityDto, TKey, TGetListInput, TEntityDto> where TEntity : class, IPrivateEntity, new()
    {
        protected CrudService(IRepository<TEntity> repository) : base(repository)
        {
        }
    }

    public abstract class CrudService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput> :
        CrudService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput, TCreateInput> where TEntity : class, IPrivateEntity, new()
    {
        protected CrudService(IRepository<TEntity> repository) : base(repository)
        {
        }
    }

    public abstract class CrudService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput> :
        CrudService<TEntity, TEntityDto, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput> where TEntity : class, IPrivateEntity, new()
    {
        protected CrudService(IRepository<TEntity> repository) : base(repository)
        {
        }
    }

    public abstract class CrudService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput> :
        ICrudService<TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput> where TEntity : class, IPrivateEntity, new()
    {
        protected readonly IRepository<TEntity> _repository;
        private readonly IMapper _mapper;

        protected CrudService(IRepository<TEntity> repository)
        {
            _repository = repository;
            _mapper = App.GetService<IMapper>();
        }

        public virtual async Task<TGetOutputDto> CreateAsync(TCreateInput input)
        {
            var record = MapToEntity(input);
            var result = await _repository.InsertNowAsync(record);
            return MapToGetOutputDto(result.Entity);
        }

        public virtual Task DeleteAsync(TKey id)
        {
            return _repository.DeleteNowAsync(id);
        }

        public virtual async Task<TGetOutputDto> GetAsync(TKey id)
        {
            var result = await _repository.FindOrDefaultAsync(id);
            return MapToGetOutputDto(result);
        }


        public virtual async Task<IReadOnlyList<TGetListOutputDto>> GetListAsync(TGetListInput input)
        {
            var query = CreateFilteredQuery(input);
            var entities = await query.ToListAsync();
            return MapToGetListOutputDtos(entities);
        }

        public virtual async Task<TGetOutputDto?> UpdateAsync(TKey id, TUpdateInput input)
        {
            var entity = await _repository.FindOrDefaultAsync(id);
            if (entity != null)
            {
                MapToEntity(input, entity);
                await _repository.UpdateNowAsync(entity);
                return MapToGetOutputDto(entity);
            }

            return default;
        }

        protected virtual IQueryable<TEntity> CreateFilteredQuery(TGetListInput input)
        {
            return _repository.AsQueryable().OrderBy("Id Desc");
        }

        protected virtual TEntity MapToEntity(TCreateInput createInput)
        {
            return _mapper.Map<TCreateInput, TEntity>(createInput);
        }

        protected virtual void MapToEntity(TUpdateInput updateInput, TEntity entity)
        {
            _mapper.Map(updateInput, entity);
        }

        protected virtual TGetOutputDto MapToGetOutputDto(TEntity entity)
        {
            return _mapper.Map<TEntity, TGetOutputDto>(entity);
        }

        protected virtual List<TGetListOutputDto> MapToGetListOutputDtos(List<TEntity> entities)
        {
            return _mapper.Map<List<TEntity>, List<TGetListOutputDto>>(entities);
        }
    }
}
